package com.xiashitech.agent.instrumentation.base.propagator;

import com.xiashitech.agent.instrumentation.base.context.XSBaggage;
import com.xiashitech.agent.instrumentation.base.context.XSBaggageBuilder;
import com.xiashitech.agent.instrumentation.base.context.XSContext;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.api.baggage.BaggageEntry;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.api.internal.StringUtils;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.propagation.TextMapSetter;

import static java.util.Collections.singletonList;

import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;

public class W3CReentryBaggagePropagator implements ReentryTextMapPropagator {

    private static final String FIELD = "xs-reentry-baggage";
    private static final String SEPARATOR = "-#x#-#s#-";
    private static final List<String> FIELDS = singletonList(FIELD);
    private static final W3CReentryBaggagePropagator INSTANCE = new W3CReentryBaggagePropagator();
    private static final XSPercentEscaper URL_ESCAPER = XSPercentEscaper.create();

    public static W3CReentryBaggagePropagator getInstance() {
        return INSTANCE;
    }

    private W3CReentryBaggagePropagator() {}

    @Override
    public Collection<String> fields() {
        return FIELDS;
    }

    /**
     * 将当前XSBaggage和由下游汇聚而来的XSBaggage一起放入carrier
     * @param context
     * @param carrier
     * @param setter
     * @param <C>
     */
    @Override
    public <C> void inject(XSContext context, @Nullable C carrier, TextMapSetter<C> setter) {
        if (context == null || setter == null) {
            return;
        }
        XSBaggage baggageForCurrent = XSBaggage.fromContext(context);
        XSBaggage[] baggageForConfluence = XSBaggage.fromContextForConfluence(context);
        String headerContent = "";
        if(!baggageForCurrent.isEmpty()) {
            headerContent = baggageToString(baggageForCurrent);
        }
        for (XSBaggage xsBaggage: baggageForConfluence) {
            if(headerContent.isEmpty()) {
                headerContent = baggageToString(xsBaggage);
            } else {
                headerContent += SEPARATOR + baggageToString(xsBaggage);
            }
        }

        if (!headerContent.isEmpty()) {
            setter.set(carrier, FIELD, headerContent);
        }
    }

    private static String baggageToString(XSBaggage baggage) {
        StringBuilder headerContent = new StringBuilder();
        baggage.forEach(
                (key, baggageEntry) -> {
                    if (baggageIsInvalid(key, baggageEntry)) {
                        return;
                    }
                    headerContent.append(key).append("=").append(encodeValue(baggageEntry.getValue()));
                    String metadataValue = baggageEntry.getMetadata().getValue();
                    if (metadataValue != null && !metadataValue.isEmpty()) {
                        headerContent.append(";").append(encodeValue(metadataValue));
                    }
                    headerContent.append(",");
                });

        if (headerContent.length() == 0) {
            return "";
        }

        // Trim trailing comma
        headerContent.setLength(headerContent.length() - 1);
        return headerContent.toString();
    }

    private static String encodeValue(String value) {
        return URL_ESCAPER.escape(value);
    }

    /**
     * 从carrier提取所有下游的XSBaggage
     * @param context
     * @param carrier
     * @param getter
     * @return
     * @param <C>
     */
    @Override
    public <C> XSContext extract(XSContext context, @Nullable C carrier, TextMapGetter<C> getter) {

        if (context == null) {
            return XSContext.root();
        }
        if (getter == null) {
            return context;
        }

        String baggageHeader = getter.get(carrier, FIELD);
        if (baggageHeader == null) {
            return context;
        }
        if (baggageHeader.isEmpty()) {
            return context;
        }
        for (String baggageHeaderItem : baggageHeader.split(SEPARATOR)) {
            XSBaggageBuilder baggageBuilder = XSBaggage.builder();
            System.out.println("###baggage extract baggageHeaderItem "+baggageHeaderItem);
            try {
                extractEntries(baggageHeaderItem, baggageBuilder);
            } catch (RuntimeException e) {
                return context;
            }
            context = context.withForConfluence(baggageBuilder.build());
        }
        return context;
    }

    private static void extractEntries(String baggageHeader, XSBaggageBuilder baggageBuilder) {
        new XSParser(baggageHeader).parseInto(baggageBuilder);
    }

    private static boolean baggageIsInvalid(String key, BaggageEntry baggageEntry) {
        return !isValidBaggageKey(key) || !isValidBaggageValue(baggageEntry.getValue());
    }

    /**
     * Determines whether the given {@code String} is a valid entry key.
     *
     * @param name the entry key name to be validated.
     * @return whether the name is valid.
     */
    private static boolean isValidBaggageKey(String name) {
        return name != null && !name.isEmpty() && StringUtils.isPrintableString(name);
    }

    /**
     * Determines whether the given {@code String} is a valid entry value.
     *
     * @param value the entry value to be validated.
     * @return whether the value is valid.
     */
    private static boolean isValidBaggageValue(String value) {
        return value != null;
    }
}